<?php
/*
+ --------------------------------------------------- +
|  Ineo Article Manager - Version 1.x
|  Copyright © 2005-2008, Dan Cryer and William McGann
+ --------------------------------------------------- +

Ineo Article Manager is free software; you can redistribute it and/or modify
it under the terms of version 3 of the GNU General Public License as published by
the Free Software Foundation.

Ineo Article Manager is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Ineo; see the file License.txt.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/**
* Ineo - Modules - Global
*  
* This module handles all non-specific front end actions.
*
* @package		Ineo
* @subpackage   Modules
* @author		Dan Cryer & William McGann
* @copyright	Dan Cryer & William McGann
* @version		1.0.0
*/
class ineo_mod_global
{
  var $ipsclass;
  var $core;
  
  function module_init()
  {
    $this->lib_global =& $this->core->load_library('global');
    $this->core->load_template('global');
    if ( $this->ipsclass->vars['ams_idx_cats'] == 1 )
    {
      $this->lib_cat =& $this->core->load_library('categories');
		  $this->core->load_template('categories');
    }
  }
  
  function index()
  {
    if ( $this->ipsclass->vars['ams_idx_cats'] == 1 )
    {
		  // Use category library to get an array of categories ( with parent 0 ).
		  $categories = $this->lib_cat->load_cats_by_parent(0);
				
		  $list = '';
		
		  // Loop & build list:
		  foreach( $categories as $cat )
		  {
			  $list .= $this->core->templates['categories']->cat_row( $cat );
		  }
		
		  // Output list using main template:
		  $category_list = $this->core->templates['categories']->cat_index( $list , $this->ipsclass->lang['ams_categories'] );
    }
    
    if ( $this->ipsclass->vars['ams_index_count'] > 0 )
    {
      // Create the recent articles lists ( latest and updated ):
		  $new = array( 'newest'  => $this->recent_articles('article_created_date'),
		                'updated' => $this->recent_articles('article_edited_date' ) );
		
		  $new = $this->core->templates['global']->recent_box( $new );
    }
    			
		// Build teh stats:		
		$users_online = $this->stats();
		
		// Pass to ams index tpl:
		$this->core->out .= $this->core->templates['global']->index( $new , $category_list , $users_online );
  }
  
	function recent_articles( $column_name )
	{
		// Query:
		$this->ipsclass->DB->simple_construct( array(
														'select' => 'article_id,article_title,article_edited_date',
														'from'   => 'ams_articles',
														'where' => 'article_is_queued = \'0\'',
													)
											);
		$this->ipsclass->DB->simple_order( $column_name . ' DESC' );
		$this->ipsclass->DB->simple_limit( $this->ipsclass->vars['ams_index_count'] );									
		$list = $this->ipsclass->DB->simple_exec();
		
		$articles = '';
		
		// Build list using the template:
		while( $res = $this->ipsclass->DB->fetch_row( $list ) )
		{
			// Make the date from timestamp:
			$res['article_date'] = $this->ipsclass->get_date($res['article_edited_date'],'');
			
			$articles .= $this->core->templates['global']->recent_articles_entry( $res );
		}
		
		return $articles;
	}
	
	/**
	* @todo Muchness needed - api seedin's
	**/
	function stats()
	{
		// Load Sessions, Whee!
		$this->ipsclass->load_language('lang_boards');
		
		$time = time() - ( $this->ipsclass->vars['au_cutoff'] * 60 );
		
		$this->ipsclass->DB->simple_construct(
									array('select' => 's.*,g.prefix,g.suffix',
										  'from'   => array( 'sessions' => 's' ),
										  'where'  => 'location = \'mod:ineo\' AND running_time > ' . $time . ' AND member_id != ' . $this->ipsclass->member['id'],
										  'add_join' => array( 1 => 
										  						array(	'type' => 'left',
										  					  			'from' => array('groups' => 'g'), 
										  					  			'where' => 'g.g_id=s.member_group',
										  					  		 )
										  					  ),
										  )
										     );
										     
		$this->ipsclass->DB->simple_order( 'running_time DESC' );
		$squery = $this->ipsclass->DB->simple_exec();
		
		//----
		// Create the array we'll pass to the template:
		//----
		$sess_data = array(
							'members' => 0,
							'guests'  => 0,
							'anon'    => 0,
							'list'    => '',
						  );
						  
		// Member name cache ( no double printing ):
		$cached = array();
				
		$ams_sessions = array();
		
		if ( $this->ipsclass->member['id'] != 0 )
		{
			$anon = ($this->ipsclass->member['login_anonymous'] == '0&1') ? 0 : 1;
			
			$ams_sessions[ md5( $this->ipsclass->member['id'] ) ] = array( 
								'login_type'   => $anon,
								'member_id'    => $this->ipsclass->member['id'],
								'member_name'  => $this->ipsclass->member['members_display_name'],
								'member_group' => $this->ipsclass->member['mgroup'],
								'location_1_type' => 'idx',
								'running_time' => time(),
								'prefix' => $this->ipsclass->member['prefix'],
								'suffix' => $this->ipsclass->member['suffix'],
								);
		}
		else
		{			
			$ams_sessions[ md5(uniqid('ineo')) ] = array( 
								'login_type'   => 0,
								'running_time' => time(),
								'member_id'    => 0,
								'member_name'  => 'Guest',
								'member_group' => $this->ipsclass->member['mgroup'],
								'location_1_type' => $this->ipsclass->input['op'],
								'prefix' => $this->ipsclass->member['prefix'],
								'suffix' => $this->ipsclass->member['suffix'],
								);
		}
		
		while( $session = $this->ipsclass->DB->fetch_row( $squery ) )
		{
			$ams_sessions[$session['id']] = $session;
		}
								
		//----
		// Populate the array:
		//----
			
		foreach( $ams_sessions as $session )
		{
				
			if( $session['member_id'] == 0 )
			{
				// Guest:
				$sess_data['guests']++;
			}
			else
			{			
				if( !isset( $cached[$session['member_id']] ) )
				{
					// Member:
					if( $session['login_type'] != 1 )
					{
						// Regular member:
						$sess_data['members']++;
						$show = 1;
					}
					else
					{
						// Anonymous:
						$sess_data['anon']++;
						$show = 0;
					}
					
					// Show in list if they are not anon, OR if the active user is a root admin:
					if( $show == 1 or ( $show == 0 && $this->ipsclass->member['g_ams_ismod'] == 1 ) )
					{
						// Add a comma first, unless it is the first run:
						if( isset( $one )) 
						{ 
							$sess_data['list'] .= ', '; 
						}
						$one = 1;
						
						// Create the 'title=' value:
						$time = $this->ipsclass->get_time( $session['running_time'] );
						
						$action = $this->ipsclass->lang['ams_act_' . $session['location_1_type']];
						
						$alt = $action . " - " . $time;
						
					
						// Create a list entry:
						$sess_data['list'] .= '<a title=\''.$alt.'\' href=\''.$this->ipsclass->base_url.'showuser=' . $session['member_id'] . '\'>' . $session['prefix'] .  $session['member_name'] . $session['suffix'] . '</a>';
					
						// Add a * for anon user:
						$sess_data['list'] .= ( $show == 0 ) ? '*' : '';
					}
				}
				else
				{
					// User is already in the list, count this one as a guest:
					$sess_data['guests']++;
				}
			}
		}
				
		// Add up all the counts for the total:
		$sess_data['total'] = $sess_data['guests'] + $sess_data['members'] + $sess_data['anon'];
		
		//----
		// Create statistics:
		//----
		
		// Article count:
		$this->ipsclass->DB->simple_construct( array( 'select' => 'count( article_id ) as articles' , 'from' => 'ams_articles', 'where' => 'article_is_queued = 0' ) );
		$count = $this->ipsclass->DB->simple_exec();
		$count = $this->ipsclass->DB->fetch_row( $count );
		
		$count = $count['articles'];
		$stats['articles'] = $count;
		
		// Get the latest article:
		
		$this->ipsclass->DB->simple_construct(
									array('select' => 'a.* , m.members_display_name',
										  'from'   => array( 'ams_articles' => 'a' ),
										  'order by' => 'a.article_id DESC',
										  'where' => 'a.article_is_queued = \'0\'',
										  'add_join' => array( 1 => 
										  						array(	'type' => 'left',
										  					  			'from' => array('members' => 'm'), 
										  					  			'where' => 'm.id=a.article_author',
										  					  		 )
										  					  ),
										  )
										     );
										     
		$this->ipsclass->DB->simple_order( 'a.article_id DESC' );
		$this->ipsclass->DB->simple_limit( 1 );									
		
		$article = $this->ipsclass->DB->simple_exec();
		$article = $this->ipsclass->DB->fetch_row($article);
		
		if( !is_numeric($article['article_author']) )
		{
			$article['members_display_name'] = $article['article_author'];
			$article['article_author'] = 0;
		}
		
		$stats['latest_title'] = $article['article_title'];
		$stats['latest_user']  = $article['members_display_name'];
		$stats['latest_id']    = $article['article_id'];
		$stats['latest_uid']   = $article['article_author'];
		
		//----
		// Send it to the template:
		//----
		
		$tmp = $this->core->templates['global']->users_online( $sess_data , $stats );
		
		// Replace 'Users in the last %s minutes' with 'last 15 minutes' or whatever it is:
				
		return str_replace( '%s' , $this->ipsclass->vars['au_cutoff'] , $tmp );
	}
	
	function art_watchlist()
	{
    // Article Specific Query:
	  $watch['title'] = 'Article';
	  $watch['op']    = 'article';
			
		$query = array( 'select' => 'w.* , a.article_title',
							      'from'   => array( 'ams_watchlists' => 'w' ),
							      'where'  => 'watch_type = 1 AND watch_user = ' . $this->ipsclass->member['id'],
							      'add_join' => array( 1 => array( 'type' => 'left',
															           'from' => array( 'ams_articles' => 'a' ),
															           'where' => 'a.article_id = w.watch_id',)
												               )
						      );
	  $this->ipsclass->DB->simple_construct( $query );
		$this->ipsclass->DB->simple_order( 'a.article_title ASC' );
		$this->ipsclass->DB->simple_exec();
		
		$wlist = '';	
		
		while( $res = $this->ipsclass->DB->fetch_row() )
		{
			$wlist .= $this->core->templates['global']->watchlist_row( $watch , $res );
		}
		
		$this->core->out .= $this->core->templates['global']->watchlist_index( $watch , $wlist );
	}
	
	function cat_watchlist()
	{
		$watch['title'] = $this->ipsclass->lang['ams_watch_category'];
		$watch['op']    = 'cat';
		
		$query = array( 'select' => 'w.* , c.category_name',
							      'from'   => array( 'ams_watchlists' => 'w' ),
							      'where'  => 'watch_type = 2 AND watch_user = ' . $this->ipsclass->member['id'],
							      'add_join' => array( 1 => array(
                                         'type' => 'left',
															           'from' => array( 'ams_categories' => 'c' ),
															           'where' => 'c.category_id = w.watch_id',)
												               )
						      );
		$this->ipsclass->DB->simple_construct( $query );
		$this->ipsclass->DB->simple_order( 'c.category_name ASC' );
		$this->ipsclass->DB->simple_exec();
		
		$wlist = '';	
		
		while( $res = $this->ipsclass->DB->fetch_row() )
		{
			$wlist .= $this->core->templates['global']->watchlist_row( $watch , $res );
		}
		
		$this->core->out .= $this->core->templates['global']->watchlist_index( $watch , $wlist );
	}
	
	function watch_add_art()
	{
		$id = $this->ipsclass->input['id'];
		
		// Firstly, delete if there already is one -
		// why they'd want to re-add is beyond me, however.
		
		$this->ipsclass->DB->simple_delete(
											'ams_watchlists' , 
											'watch_user = ' . $this->ipsclass->member['id'] .
											' AND watch_id = ' . $id .
											' AND watch_type = 1'
										  );
		$this->ipsclass->DB->simple_exec();
		
		// Then add the watch:
		
		$this->ipsclass->DB->do_insert( 
											'ams_watchlists',
											array( 
													'watch_user' => $this->ipsclass->member['id'],
													'watch_id'   => $id,
													'watch_type' => 1,
												)
											);
		$this->ipsclass->boink_it( $this->core->base_url . 'showarticle=' . $id . '&msg=ams_sys_artwatch' );
	}
	
	function watch_add_cat()
	{
		$id = $this->ipsclass->input['id'];
		
		// Firstly, delete if there already is one -
		// why they'd want to re-add is beyond me, however.
		
		$this->ipsclass->DB->simple_delete(
											'ams_watchlists' , 
											'watch_user = ' . $this->ipsclass->member['id'] .
											' AND watch_id = ' . $id .
											' AND watch_type = 2'
										  );
		$this->ipsclass->DB->simple_exec();
		
		// Then add the watch:
		
		$this->ipsclass->DB->do_insert( 
											'ams_watchlists',
											array( 
													'watch_user' => $this->ipsclass->member['id'],
													'watch_id'   => $id,
													'watch_type' => 2,
												)
											);
		$this->ipsclass->boink_it( $this->core->base_url . 'showcat=' . $id . '&msg=ams_sys_catwatch' );
	}
	
	function watch_cat_delete()
	{
		$this->ipsclass->DB->simple_delete(
											'ams_watchlists' , 
											'watch_user = ' . $this->ipsclass->member['id'] .
											' AND watch_id = ' . $this->ipsclass->input['id'] .
											' AND watch_type = 2'
										  );
		$this->ipsclass->DB->simple_exec();
		$this->ipsclass->boink_it( $this->core->base_url . 'mod=global&op=cat_watchlist&msg=ams_catwatch_del' );
	}
	
	function watch_art_delete()
	{
		$this->ipsclass->DB->simple_delete(
											'ams_watchlists' , 
											'watch_user = ' . $this->ipsclass->member['id'] .
											' AND watch_id = ' . $this->ipsclass->input['id'] .
											' AND watch_type = 1'
										  );
		$this->ipsclass->DB->simple_exec();
		$this->ipsclass->boink_it( $this->core->base_url . 'mod=global&op=art_watchlist&msg=ams_artwatch_del' );
	}
	
	/**
	* Full Search
	*/
	function full_search()
	{
		$catLib =& $this->core->load_library('categories');
		
		$cat_list = $catLib->select_list_items();
		$this->core->out .= $this->core->templates['skin_ams_global']->search_page( $cat_list );
	}
	
	/**
	* Do full search
	*/
	function full_search_do()
	{
		$db =& $this->ipsclass->DB;
		
		//----
		// Get categories to search:
		//----
		$categories = $this->ipsclass->input['categories'];
		$categories = in_array('0',$categories) ? '' : ' AND a.article_cat_id IN (0,'.$this->safe_int_ids($categories).')';
		
		//----
		// Get keywords to search:
		//----
		$keys = $this->ipsclass->input['keywords'];
		$keywords = $this->filter_ftext_keywords($this->ipsclass->input['keywords']);
		$keywords = trim($keywords);
		
		//----
		// Start / Limit / Order :
		//----
		$order_in    = $this->ipsclass->input['order'];
		$start = intval( $this->ipsclass->input['st'] );
		$order = ( $order_in == 'relevance' ) ? 'relevance' : 'c.content_date';
		
		//----
		// Bool Mode?
		//----
		$this->ipsclass->DB->sql_get_version();
		$bool = ( $this->ipsclass->DB->mysql_version >= 40010 ) ? 'IN BOOLEAN MODE' : '';
		
		//----
		// Queued Articles?
		//----
		$queued = ($this->ipsclass->member['g_ams_ismod']) ? '' : ' AND a.article_is_queued = 0';
		
		
		//--------------------
		// Get result count:
		
		$query = "SELECT count( c.content_content ) as result_count FROM ".SQL_PREFIX."ams_content c LEFT JOIN ".SQL_PREFIX."ams_articles a ON ( a.article_content = c.content_id ) WHERE MATCH( c.content_content ) AGAINST ('{$keywords}' {$bool}) AND a.article_title IS NOT NULL {$queued}{$categories}";
		
		$db->query( $query );	
		
		$res = $db->fetch_row();
		
		if( $res['result_count'] == 0 )
		{
			$this->core->error('survey_says_uhuh',$this->ipsclass->lang['ams_gen_error']);
			return;
		}
		else
		{
			$search_count = $res['result_count'];
		}
		//--------------------
		
		//--------------------
		// Perform search query:
		$query = "SELECT MATCH( c.content_content ) AGAINST ('{$keywords}' {$bool}) as relevance, c.*, a.* FROM ".SQL_PREFIX."ams_content c LEFT JOIN ".SQL_PREFIX."ams_articles a ON ( a.article_content = c.content_id ) WHERE MATCH( c.content_content ) AGAINST ('{$keywords}' {$bool}) AND a.article_title IS NOT NULL {$queued}{$categories} ORDER BY {$order} DESC LIMIT {$start},20";
		
		$query = $db->query( $query );
		//--------------------
		
		$info = array(
						'string' => 'op=full_search_do&keywords=' . $keys,
						'total'  => $search_count,
						'start'  => $start,
					 );
					
		$this->search_results_from_query($query,$info);
		
	}
	
	function author_search_do()
	{
		if( empty($this->ipsclass->input['author']) )
		{
			$this->core->error('survey_says_uhuh',$this->ipsclass->lang['ams_gen_error']);
			return;
		}
		
		$start = empty($this->ipsclass->input['st']) ? 0 : intval($this->ipsclass->input['st']);
		
		$username = mysql_real_escape_string($this->ipsclass->input['author']);
		
		$this->ipsclass->DB->simple_construct( array('select' => 'id' , 'from' => 'members' , 'where' => 'members_display_name = \''.$username.'\'') );
		$mem = $this->ipsclass->DB->simple_exec();
		
		if( $this->ipsclass->DB->get_num_rows() == 0 )
		{
			$this->core->error('survey_says_uhuh',$this->ipsclass->lang['ams_gen_error']);
			return;
		}
		
		$mem = $this->ipsclass->DB->fetch_row();
		$member_id = $mem['id'];
		
		$this->ipsclass->DB->simple_construct( 
												array( 
														'select' => 'count( c.content_id ) as result_count' , 
														'from' => array('ams_content'=>'c'),
														'add_join' => array( 1 => 
																					array( 
																						'type' => 'left',
																						'from' => array('ams_articles' => 'a'),
																						'where' => 'a.article_content = c.content_id',	
																						)
																			),
														'where' => 'c.content_author = ' . $member_id . ' and a.article_title IS NOT NULL',
													)
												);
		$query = $this->ipsclass->DB->simple_exec();
		$res = $this->ipsclass->DB->fetch_row();
		
		if( $res['result_count'] == 0 )
		{
			$this->core->error('survey_says_uhuh',$this->ipsclass->lang['ams_gen_error']);
			return;
		}
		else
		{
			$result_count = $res['result_count'];
		}
		
		$this->ipsclass->DB->simple_construct( 
												array( 
														'select' => 'c.*,a.*' , 
														'from' => array('ams_content'=>'c'),
														'add_join' => array( 1 => 
																					array( 
																						'type' => 'left',
																						'from' => array('ams_articles' => 'a'),
																						'where' => 'a.article_content = c.content_id',	
																						)
																			),
														'where' => 'c.content_author = ' . $member_id . ' and a.article_title IS NOT NULL',
													)
												);
		$this->ipsclass->DB->simple_order('c.content_date DESC');
		$this->ipsclass->DB->simple_limit($start.', 20');
		$query = $this->ipsclass->DB->simple_exec();
		
		$info = array(
						'string' => 'op=author_search_do&author=' . $username,
						'total'  => $result_count,
						'start'  => $start,
					 );
		
		$this->search_results_from_query($query,$info);
	}
	
	function search_results_from_query( $query , $search_info )
	{
		//--------------------
		// Loop results and use templates for row HTML:
		$results = '';
		
		while( $article = $this->ipsclass->DB->fetch_row( $query ) )
		{
			$article['article_date_text'] = $this->ipsclass->get_date($article['article_edited_date'],'');
			
			$results .= $this->core->templates['skin_ams_global']->full_search_results_row( $article );
		}
		//--------------------
		
		
		//--------------------
		// Build page links and dump them + results into the search results template:
		$pages = $this->ipsclass->build_pagelinks( array( 'TOTAL_POSS' => $search_info['total'],
			                                              'PER_PAGE'   => 20,
			                                              'CUR_ST_VAL' => $search_info['start'],
			                                              'BASE_URL'   => "{$this->ipsclass->base_url}autocom=ineo&mod=global&{$search_info['string']}"
														) 
													);
		
		$this->core->out .= $this->core->templates['skin_ams_global']->full_search_results( $results , $pages );
		//--------------------
	}
	
	/**
	* Takes an array of category ID's and intvals them all, then implodes with commas.
	*/
	function safe_int_ids($array)
	{
		$safe = array();
		
		foreach( $array as $val )
		{
			$safe[] = intval($val);
		}
		
		return implode(',',$safe);
	}
	
	/**
	* Borrowed from IPB. :/
	*/
	function filter_ftext_keywords($words="")
    {
		// force to lowercase and swop % into a safer version
    	
    	$words = trim( rawurldecode($words) );
    	$words = str_replace( '|', "&#124;", $words );
    	
    	// Remove crap
    	
    	$words = str_replace( "&quot;", '"', $words );
    	$words = str_replace( "&gt;"  , ">", $words );
    	$words = str_replace( "%"     , "" , $words );
    	
    	//-----------------------------------------
    	// If it's a phrase in quotes..
    	//-----------------------------------------
    	
    	if ( preg_match( "#^\"(.+?)\"$#", $words ) )
    	{
    		return $words;
    	}
    	
    	// Remove common words..
    	
    	$words = preg_replace( "/^(?:img|quote|code|html|javascript|a href|color|span|div|border|style)$/", "", $words );
    	
    	// OK, lets break up the keywords
    	
    	// this or that and this not me
    	
    	$words = preg_replace( "/\s+and\s+/i", " ", $words );
    	
    	// this or that this not me
    	
    	$words = preg_replace( "/\s+not\s+/i", " -", $words );
    	
    	// this or that this -me
    	
    	$words = preg_replace( "/\s+or\s+/i", ' ~', $words );
    	
    	// this ~that this -me
    	
    	# Was added as a bug fix but really this causes more problems
    	# than it solves. Complaint was that it should default to AND
    	# matching, not OR matching. Problem is that it doesn't then
    	# give a "true" search as one would expect Google to do.
    	
    	//$words = preg_replace( "/\s+(?!-|~)/", " +", $words );
    	
    	
    	// this ~that +this -me
    	
    	$words = preg_replace( "/~/", "", $words );
    	
    	// this that +this -me
    	
    	return "+".$words;
    }
}

?>